package org.webdb.jdbc;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.webdb.core.config.DatabaseConfig;

import com.healthmarketscience.sqlbuilder.dbspec.Table;

public class JDBCTable {

    public static final JDBCTable NOT_LOADED = new JDBCTable("#NEW#", "#NEW#");

    private static final Pattern COLUMN_CONFIG_PATTERN = Pattern
            .compile("([^:]+):(.+)");

    protected ArrayList<JDBCColumn> columns = new ArrayList<JDBCColumn>(1);
    private String name;
    private String schema;
    private Table sqlTable;

    private List<JDBCColumn> primaryKeys;

    public JDBCTable(String schemaName, String tableName) {
        this.schema = schemaName;
        this.name = tableName;
    }

    public JDBCTable(String schemaName, String tableName,
            DatabaseMetaData jdbcMetaData, DatabaseConfig config) {
        this(schemaName, tableName);
        try {
            jdbcMetaData.supportsGetGeneratedKeys();
            loadColumns(jdbcMetaData, schemaName, tableName);
            if (config != null)
                configure(config);
        } catch (Exception e) {
            throw new JDBCException(toString() + " Loading columns", e);
        }
    }

    private void configure(DatabaseConfig config) {
        configureColumns(config);
    }

    private void configureColumns(DatabaseConfig config) {
        for (Entry<String, Object> entry : config.entrySet()) {
            JDBCColumn column = searchColumn(entry.getKey());
            if (column != null) {
                // Melhorar...
                Matcher matcher = COLUMN_CONFIG_PATTERN.matcher(entry
                        .getValue().toString());

                if (matcher.find()) {
                    if ("SEQUENCE".equalsIgnoreCase(matcher.group(1)))
                        column.setStrategy(new SequenceStrategy(matcher
                                .group(2)));
                }
            }
        }
    }

    @Override
    public String toString() {
        return JDBCTable.class.getSimpleName() + "[" + schema + "." + name
                + "]";
    }

    private void loadColumns(DatabaseMetaData jdbcMetaData, String schemaName,
            String tableName) throws SQLException {
        ResultSet rs = jdbcMetaData.getColumns(null, schemaName, tableName,
                null);
        try {
            while (rs.next()) {
                columns.add(new JDBCColumn(rs, schema, name));
            }
            JDBCUtils.close(rs);

            rs = jdbcMetaData.getPrimaryKeys(null, schemaName, tableName);
            LinkedList<JDBCColumn> pks = new LinkedList<JDBCColumn>();
            while (rs.next()) {
                JDBCColumn column = searchColumn(rs.getString(4));
                if (column != null) {
                    column.setPrimaryKey(true);
                    pks.add(column);
                } else
                    throw new JDBCException(toString() + " Column "
                            + rs.getString(4) + " not found!");
            }
            primaryKeys = new ArrayList<JDBCColumn>(pks);
        } finally {
            JDBCUtils.close(rs);
        }
    }

    public JDBCColumn searchColumn(String columnName) {
        for (JDBCColumn jdbcColumn : columns)
            if (columnName.equalsIgnoreCase(jdbcColumn.getName()))
                return jdbcColumn;

        return null;
    }

    public void addColumn(JDBCColumn column) {
        columns.add(column);
        columns.trimToSize();
    }

    public String getName() {
        return name;
    }

    public String getSchema() {
        return schema;
    }

    public Table getSqlTable() {
        return sqlTable;
    }

    public List<JDBCColumn> getPrimaryKeys() {
        return primaryKeys;
    }

    public List<JDBCColumn> getColumns() {
        return columns;
    }

    public JDBCColumn getColumn(String columnName) {
        JDBCColumn column = searchColumn(columnName);
        if (column == null)
            throw new JDBCException(toString() + " Column " + columnName
                    + " not found, supported names are ["
                    + JDBCUtils.getColumnNames(this) + "]");
        return column;
    }

    public boolean hasPrimaryKeys() {
        return !primaryKeys.isEmpty();
    }

}
